from __future__ import with_statement
import string
import os.path

class ParseError(Exception): pass
class IncompatibleArguments(ParseError): pass
class MismatchedBrackets(ParseError): pass
class MismatchedQuotes(ParseError): pass
class UnboundVariable(ParseError): pass

def line(text, var_func):
	if text.count('(') != text.count(')'):
		raise MismatchedBrackets(text)
		
	if text.count('"') % 2:
		raise MismatchedQuotes(text)

	results = []
	
	while text:		
		if text[0] in ', ':
			text = text[1:]
			
			end = 0
			result = None		
		elif text[0] in string.digits or text[0] in "-+":			
			end = text.find(',') + 1
			
			if end == 0:
				end = text.find('#') + 1
				
				if end == 0:
					result = text
					end = len(text)
				else:
					result = text[:end - 1]
			else:
				result = text[:end - 1]
				
			result = int(result)		
		elif text[0] == '(':
			end = 0
			layer = 0
			
			for index, char in enumerate(text):
				if char == "(":
					layer += 1
				elif char == ")":
					layer -= 1
					if layer == 0:
						end = index + 1
						break
			
			result = tuple(line(text[1:end - 1], var_func))		
		elif text[0] == '"':		
			end = text[1:].find('"') + 2	
			result = text[1:end - 1]
		else:
			end = text.find(',') + 1
			
			if end == 0:
				end = text.find('#') + 1
				
				if end == 0:
					variable = text
					end = len(text)
				else:
					variable = text[:end - 1]
			else:
				variable = text[:end - 1]
			
			result = var_func(variable)
			
		if result is not None:
			results.append(result)	
		
		text = text[end:]
		
	return results

def section(lines, objects, globals={}, operators={}):
	locals = {}
	
	def get_var(name):
		if locals.has_key(name):
			return locals[name]
		elif globals.has_key(name):
			return globals[name]
		else:
			raise UnboundVariable("'%s' does not exist." % name)
	
	for line_ in lines:
		if line_.startswith('"'):
			filename = "%s.txt" % line_[1:-1]
				
			try:
				with open(filename) as file:
					new_globals = globals.copy()
					new_globals.update(locals)
					
					locals.update(section(file, objects, new_globals, operators))
					
			except IOError:
				print "Could not find '%s' file." % filename
				
		elif ":" in line_:
			name, arguments = (part.strip() for part in line_.split(":"))
			
			if "=" in name:
				var_name, name = (part.strip() for part in name.split("="))
			else:
				var_name = None
			
			try:
				object_type = objects[name]
			except KeyError:
				try:
					object_type = get_var(name)
				except UnboundVariable:
					print "Unknown object: '%s'" % name
					break
			
			args = line(arguments, get_var)
			
			#try:
			object = object_type(*args)
			#except TypeError:
			#	msg = "'%s' object does not take arguments in the form '%s'" % (
			#		name, str(arguments))
			#	
			#	raise IncompatibleArguments(msg)
			
			if var_name is not None:
				locals[var_name] = object
		
		elif "->" in line_:
			a, b = (part.strip() for part in line_.split("->"))
			
			operators["->"](get_var(a), get_var(b))
	
	return locals

def file(file_, parsers, import_dir="."):				
	for section, lines in sections(file_, import_dir).iteritems():
		parsers[section](lines)

def sections(file, import_dir="./"):
	parser = Parser(file, import_dir)
	return parser.sections
	
def details(details):
	def parser(lines):
		class Objects(dict):
			def __missing__(self, key):
				def set_value(*values):
					if len(values) > 1:
						details[key] = values
					else:
						details[key] = values[0]
				
				return set_value
	
		section(lines, Objects())
		
	return parser
	
class Parser:
	def __init__(self, file=None, path="./", parsers={}):
		if not file:
			file = open(path)
		
		self.file = file
		#self.path = os.path.dirname(path)
		self.path = path
		
		self.sections = {}
		self.parsers = parsers
		
		self._load_sections()
	
	def _load_sections(self):
		section = None
		section_lines = None
		
		for line in self.file:
			line = line.rstrip("\r\n")
			
			if line.strip().startswith("#") and section != "layer":
				pass
			elif line.startswith("\t"):
				section_lines.append(line.lstrip("\t"))
			elif line.startswith("import"):
				filename = line.strip().split(" ", 1)[1][1:-1]
				filepath = "%s%s" % (os.path.join(self.path, "..", filename), ".txt")
				
				imported_sections = sections(open(filepath))
				
				for name, section_ in imported_sections.iteritems():
					if name in self.sections:
						self.sections[name].extend(section_)
					else:
						self.sections[name] = section_
			
			elif line.strip():
				if section is not None:
					self.sections[section].append(section_lines)
				
				section = line.strip()
				section_lines = []
				
				if section not in self.sections:
					self.sections[section] = []
		else:
			self.sections[section].append(section_lines)
			
	def section(self, name):
		section = self.sections[name]
		
		for lines in section:
			self.parsers[name](lines)
